/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

/*
 * ProcessFrame.java
 *
 * Created on Sep 22, 2012, 11:49:26 AM
 */
package phd.mrs.result.gui;

import java.awt.Color;
import java.awt.Font;
import java.awt.Paint;
import java.text.DateFormat;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.persistence.PersistenceException;
import javax.persistence.Query;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JTable;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.AbstractTableModel;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartMouseEvent;
import org.jfree.chart.ChartMouseListener;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.LogAxis;
import org.jfree.chart.entity.XYItemEntity;
import org.jfree.chart.event.RendererChangeEvent;
import org.jfree.chart.labels.StandardXYToolTipGenerator;
import org.jfree.chart.labels.XYToolTipGenerator;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.data.xy.XYDataItem;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
import phd.mrs.heuristic.object.Agent;
import phd.mrs.heuristic.object.Evolution;
import phd.mrs.heuristic.object.Project;

/**
 *
 * @author Vitaljok
 */
public class ProcessFrame extends JFrame {

    EntityManager entityManager;
    EntityManagerFactory entityManagerFactory;

    /**
     * Creates new form ProcessFrame
     */
    public ProcessFrame() {
        initComponents();

        try {
            entityManagerFactory = Persistence.createEntityManagerFactory("PhD_MRS_Optimization_Result_PU");
            entityManager = entityManagerFactory.createEntityManager();

        } catch (PersistenceException ex) {
            JOptionPane.showMessageDialog(null, "Error connecting to Database\n" + ex, "Fatal error", JOptionPane.ERROR_MESSAGE);
            System.exit(1);
        }

        this.statusLabel.setText("Connected to MRS database.");
    }

    /**
     * This method is called from within the constructor to initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is always
     * regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        jSplitPane1 = new javax.swing.JSplitPane();
        leftPanel = new javax.swing.JPanel();
        procToolBar = new javax.swing.JToolBar();
        procReloadBtn = new javax.swing.JButton();
        showBtn = new javax.swing.JButton();
        jScrollPane1 = new javax.swing.JScrollPane();
        projTable = new javax.swing.JTable();
        rightPanel = new javax.swing.JPanel();
        jSplitPane3 = new javax.swing.JSplitPane();
        dataPanel = new javax.swing.JPanel();
        jScrollPane2 = new javax.swing.JScrollPane();
        solutionTable = new javax.swing.JTable();
        displayPanel = new javax.swing.JPanel();
        statusPanel = new javax.swing.JPanel();
        statusLabel = new javax.swing.JLabel();

        setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
        setTitle("MRS evaluation results");
        setName("mrsFrame"); // NOI18N
        addWindowListener(new java.awt.event.WindowAdapter() {
            public void windowClosing(java.awt.event.WindowEvent evt) {
                formWindowClosing(evt);
            }
        });

        jSplitPane1.setPreferredSize(new java.awt.Dimension(1200, 800));

        leftPanel.setMaximumSize(new java.awt.Dimension(300, 32767));
        leftPanel.setPreferredSize(new java.awt.Dimension(280, 10));
        leftPanel.setLayout(new java.awt.BorderLayout());

        procToolBar.setFloatable(false);
        procToolBar.setRollover(true);

        procReloadBtn.setText("Reload processes");
        procReloadBtn.setToolTipText("");
        procReloadBtn.setFocusable(false);
        procReloadBtn.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
        procReloadBtn.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
        procReloadBtn.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                procReloadBtnActionPerformed(evt);
            }
        });
        procToolBar.add(procReloadBtn);

        showBtn.setFocusable(false);
        showBtn.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
        showBtn.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
        showBtn.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                showBtnActionPerformed(evt);
            }
        });
        procToolBar.add(showBtn);

        leftPanel.add(procToolBar, java.awt.BorderLayout.NORTH);

        projTable.setModel(projTableModel);
        projTable.setAutoResizeMode(javax.swing.JTable.AUTO_RESIZE_ALL_COLUMNS);
        projTable.setCursor(new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR));
        projTable.getSelectionModel().addListSelectionListener(procTableListener);
        jScrollPane1.setViewportView(projTable);

        leftPanel.add(jScrollPane1, java.awt.BorderLayout.CENTER);

        jSplitPane1.setLeftComponent(leftPanel);

        rightPanel.setLayout(new java.awt.BorderLayout());

        jSplitPane3.setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT);

        dataPanel.setPreferredSize(new java.awt.Dimension(452, 150));
        dataPanel.setLayout(new java.awt.BorderLayout());

        solutionTable.setAutoCreateRowSorter(true);
        solutionTable.setModel(solutionTableModel);
        solutionTable.setAutoResizeMode(javax.swing.JTable.AUTO_RESIZE_LAST_COLUMN);
        jScrollPane2.setViewportView(solutionTable);

        dataPanel.add(jScrollPane2, java.awt.BorderLayout.CENTER);

        jSplitPane3.setBottomComponent(dataPanel);

        displayPanel.setPreferredSize(new java.awt.Dimension(800, 600));
        displayPanel.setLayout(new java.awt.BorderLayout());
        jSplitPane3.setTopComponent(displayPanel);

        rightPanel.add(jSplitPane3, java.awt.BorderLayout.CENTER);

        jSplitPane1.setRightComponent(rightPanel);

        getContentPane().add(jSplitPane1, java.awt.BorderLayout.CENTER);

        statusPanel.setBorder(javax.swing.BorderFactory.createEtchedBorder());
        statusPanel.setPreferredSize(new java.awt.Dimension(150, 25));
        statusPanel.setLayout(new java.awt.BorderLayout());

        statusLabel.setToolTipText("");
        statusPanel.add(statusLabel, java.awt.BorderLayout.CENTER);

        getContentPane().add(statusPanel, java.awt.BorderLayout.SOUTH);

        pack();
    }// </editor-fold>//GEN-END:initComponents

    private void procReloadBtnActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_procReloadBtnActionPerformed
        entityManager.clear();
        Query query = entityManager.createNamedQuery("Project.findAll");
        this.projTableModel.setData(query.getResultList());

        this.projTable.setAutoResizeMode(JTable.AUTO_RESIZE_SUBSEQUENT_COLUMNS);
        this.projTable.getColumnModel().getColumn(0).setPreferredWidth(20);

    }//GEN-LAST:event_procReloadBtnActionPerformed

    private void formWindowClosing(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_formWindowClosing
        entityManager.close();
        entityManagerFactory.close();
    }//GEN-LAST:event_formWindowClosing

    private void showBtnActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_showBtnActionPerformed
        if (projTable.getSelectedRowCount() == 0) {
            return;
        }

        entityManager.clear(); // clear managed entities to get most actual

        Query query = entityManager.createNamedQuery("Evolution.findByProject");

        //<editor-fold defaultstate="collapsed" desc=" Default XY Dataset ">
//        DefaultXYDataset dataSet = new DefaultXYDataset();
//
//        for (int rowNum : projTable.getSelectedRows()) {
//            int procId = projTableModel.getData().get(rowNum).getId();
//            query.setParameter("procId", procId);
//            List<Evolution> evList = query.getResultList();
//            double[][] data = new double[2][evList.size()];
//
//
//            for (int i = 0; i < evList.size(); i++) {
//                EvolutionOld ev = evList.get(i);
//                data[1][i] = ev.getEvolutionPK().getGeneration();
//                data[0][i] = ev.getFitnessValue();
//            }
//
//            dataSet.addSeries(procId, data);
//            
//            
//        }
//
//        JFreeChart chart = ChartFactory.createScatterPlot(null,
//                "Cost",
//                "Generation",
//                dataSet,
//                PlotOrientation.HORIZONTAL,
//                true,
//                false,
//                false);
        // </editor-fold>

        XYSeriesCollection dataSet = new XYSeriesCollection();

        for (int rowNum : projTable.getSelectedRows()) {
            Project proj = projTableModel.getData().get(rowNum);
            query.setParameter("proj", proj);
            List<Evolution> evList = query.getResultList();

            XYSeries series = new XYSeries(proj.getId());

            for (Evolution ev : evList) {
                MyXYDataItem item = new MyXYDataItem(ev, ev.getFitnessValue(), ev.getGeneration());
                series.add(item);
            }

            dataSet.addSeries(series);
        }

        JFreeChart chart = ChartFactory.createXYLineChart(null,
                "Cost",
                "Generation",
                dataSet,
                PlotOrientation.HORIZONTAL,
                true,
                true,
                false);

        XYPlot plot = chart.getXYPlot();
        plot.setDomainPannable(true);
        plot.setRangePannable(true);

        LogAxis domainAxis = new LogAxis("Total cost of ownership");
        domainAxis.setNumberFormatOverride(NumberFormat.getIntegerInstance());
        domainAxis.setLabelFont(new Font("SansSerif", Font.BOLD, 14));

        LogAxis rangeAxis = new LogAxis("Generation");
        rangeAxis.setNumberFormatOverride(NumberFormat.getIntegerInstance());
        rangeAxis.setLabelFont(new Font("SansSerif", Font.BOLD, 14));

        plot.setDomainAxis(domainAxis);
        plot.setRangeAxis(rangeAxis);



        final MyXYLineAndShapeRenderer render = new MyXYLineAndShapeRenderer();

        plot.setRenderer(render);

        ChartPanel chartPanel = new ChartPanel(chart);
        //chartPanel.setMouseWheelEnabled(true);

        this.displayPanel.removeAll();
        this.displayPanel.add(chartPanel);
        this.displayPanel.getParent().validate();

        chartPanel.addChartMouseListener(new ChartMouseListener() {
            @Override
            public void chartMouseClicked(ChartMouseEvent event) {
                if (!(event.getEntity() instanceof XYItemEntity)) {
                    render.setSelect(-1, -1);
                } else {
                    XYItemEntity entity = (XYItemEntity) event.getEntity();
                    XYSeriesCollection dataSet = (XYSeriesCollection) entity.getDataset();
                    MyXYDataItem item = (MyXYDataItem) dataSet.getSeries(entity.getSeriesIndex()).getDataItem(entity.getItem());

                    showSolution((Evolution) item.getData());

                    render.setSelect(entity.getSeriesIndex(), entity.getItem());
                }
            }

            @Override
            public void chartMouseMoved(ChartMouseEvent event) {
                if (!(event.getEntity() instanceof XYItemEntity)) {
                    render.setHighlight(-1, -1);
                } else {
                    XYItemEntity entity = (XYItemEntity) event.getEntity();
                    render.setHighlight(entity.getSeriesIndex(), entity.getItem());
                }
            }
        });
    }//GEN-LAST:event_showBtnActionPerformed

    public void showSolution(Evolution evo) {
        this.solutionTableModel.setData(evo.getAgents());

        this.solutionTable.getColumnModel().getColumn(0).setMaxWidth(50);
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String args[]) {
        /* Set the Nimbus look and feel */
        //<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
        /* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel.
         * For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html 
         */
        try {
            for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
                if ("Nimbus".equals(info.getName())) {
                    javax.swing.UIManager.setLookAndFeel(info.getClassName());
                    break;
                }
            }
        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | javax.swing.UnsupportedLookAndFeelException ex) {
            java.util.logging.Logger.getLogger(ProcessFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        }
        //</editor-fold>

        /* Create and display the form */
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                new ProcessFrame().setVisible(true);
            }
        });
    }
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JPanel dataPanel;
    private javax.swing.JPanel displayPanel;
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JScrollPane jScrollPane2;
    private javax.swing.JSplitPane jSplitPane1;
    private javax.swing.JSplitPane jSplitPane3;
    private javax.swing.JPanel leftPanel;
    private javax.swing.JButton procReloadBtn;
    private javax.swing.JToolBar procToolBar;
    private javax.swing.JTable projTable;
    private javax.swing.JPanel rightPanel;
    private javax.swing.JButton showBtn;
    private javax.swing.JTable solutionTable;
    private javax.swing.JLabel statusLabel;
    private javax.swing.JPanel statusPanel;
    // End of variables declaration//GEN-END:variables
    ListSelectionListener procTableListener = new ListSelectionListener() {
        @Override
        public void valueChanged(ListSelectionEvent e) {
            showBtnActionPerformed(null);
        }
    };
    ProjectTableModel projTableModel = new ProjectTableModel();
    SolutionTableModel solutionTableModel = new SolutionTableModel();

    private class MyXYDataItem extends XYDataItem {

        Object data;

        public MyXYDataItem(Object data, Number x, Number y) {
            super(x, y);
            this.data = data;
        }

        public Object getData() {
            return data;
        }

        public void setData(Object data) {
            this.data = data;
        }
    }

    private class MyXYLineAndShapeRenderer extends XYLineAndShapeRenderer {

        int highlightSeries;
        int highlightItem;
        int selectSeries;
        int selectItem;

        public void setHighlight(int series, int item) {
            if (this.highlightItem == item && this.highlightSeries == series) {
                return;
            } else {
                this.highlightSeries = series;
                this.highlightItem = item;

                notifyListeners(new RendererChangeEvent(this));
            }
        }

        public void setSelect(int series, int item) {
            if (this.selectItem == item && this.selectSeries == series) {
                return;
            } else {
                this.selectSeries = series;
                this.selectItem = item;

                notifyListeners(new RendererChangeEvent(this));
            }
        }

        public MyXYLineAndShapeRenderer() {
            super(true, true);
            highlightSeries = -1;
            highlightItem = -1;
            selectSeries = -1;
            selectItem = -1;

            XYToolTipGenerator gen = new StandardXYToolTipGenerator("{0}: (Cost: {1}; Gen: {2})", NumberFormat.getIntegerInstance(), NumberFormat.getIntegerInstance());

            this.setBaseToolTipGenerator(gen);
            this.setBaseShapesVisible(true);
            this.setBaseShapesFilled(true);
            this.setUseOutlinePaint(true);
        }

        @Override
        public Paint getItemOutlinePaint(int row, int column) {
            if (row == this.highlightSeries && column == this.highlightItem) {
                return Color.yellow;
            } else if (row == this.selectSeries && column == this.selectItem) {
                return Color.white;
            } else {
                return Color.black;//super.getItemOutlinePaint(row, column);
            }
        }
    }

    private class ProjectTableModel extends AbstractTableModel {

        private String[] colNames = {"Id", "Start time", "End time"};
        private DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm");
        private List<Project> data = new ArrayList<>();

        public List<Project> getData() {
            return data;
        }

        public void setData(List<Project> data) {
            this.data.clear();
            this.data.addAll(data);
            this.fireTableDataChanged();
        }

        @Override
        public String getColumnName(int column) {
            return colNames[column];
        }

        @Override
        public int getRowCount() {
            return data.size();
        }

        @Override
        public int getColumnCount() {
            return colNames.length;
        }

        @Override
        public Object getValueAt(int rowIndex, int columnIndex) {

            switch (columnIndex) {
                case 0:
                    return data.get(rowIndex).getId();
                case 1:
                    return dateFormat.format(data.get(rowIndex).getStartTime());
                case 2:
                    if (data.get(rowIndex).getEndTime() != null) {
                        return dateFormat.format(data.get(rowIndex).getEndTime());
                    } else {
                        return "<running>";
                    }
                default:
                    return null;
            }
        }
    }

    private class SolutionTableModel extends AbstractTableModel {

        private List<String> colNames = new ArrayList<>(Arrays.asList("Inst.", "Solution"));
        private List<Agent> agents = new ArrayList<>();
        private List<Integer> instances = new ArrayList<>();

        public void setData(Map<Agent, Integer> data) {
            this.agents.clear();
            this.instances.clear();

            for (Agent agent : data.keySet()) {
                this.agents.add(agent);
                this.instances.add(data.get(agent));
            }

            this.fireTableDataChanged();
        }

        @Override
        public String getColumnName(int column) {
            return colNames.get(column);
        }

        @Override
        public int getRowCount() {
            return agents.size();
        }

        @Override
        public int getColumnCount() {
            return colNames.size();
        }

        @Override
        public Object getValueAt(int rowIndex, int columnIndex) {

            switch (columnIndex) {
                case 0:
                    return instances.get(rowIndex);
                case 1:
                    return agents.get(rowIndex).getCode();
                default:
                    return null;
            }
        }
    }
}
